package us.luosl.lslt.lang;

import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Function;

public class Tuple2<T1, T2> implements Tuple{

    public final T1 _1;

    public final T2 _2;

    public Tuple2(T1 left, T2 right) {
        this._1 = left;
        this._2 = right;
    }

    public T1 get_1() {
        return _1;
    }

    public T2 get_2() {
        return _2;
    }

    public <R> Tuple2<R, T2> map1(Function<T1, R> function){
        return Tuple.of(function.apply(_1), _2);
    }

    public <R> Tuple2<T1, R> map2(Function<T2, R> function){
        return Tuple.of(_1, function.apply(_2));
    }

    public <R1, R2> Tuple2<R1, R2> map(BiFunction<T1, T2, Tuple2<R1, R2>> mapper){
        return mapper.apply(_1, _2);
    }

    public <R> R apply(BiFunction<T1, T2, R> function){
        return function.apply(_1, _2);
    }

    public void accept(BiConsumer<T1, T2> consumer){
        consumer.accept(_1, _2);
    }

    public boolean test(BiPredicate<T1, T2> predicate){
        return predicate.test(_1, _2);
    }

    public Tuple2<T2, T1> reverse(){
        return Tuple.of(_2, _1);
    }

    @Override
    public int hashCode() {
        int result = 1;
        result = 31 * result + Objects.hashCode(_1);
        result = 31 * result + Objects.hashCode(_2);
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        } else if (!(obj instanceof Tuple2)) {
            return false;
        } else {
            final Tuple2<?, ?> that = (Tuple2<?, ?>) obj;
            return Objects.equals(this._1, that._1)
                    && Objects.equals(this._2, that._2);
        }
    }
}
